#include "tr_definitions.h"
#include "tr_common.h"

static VECTOR src_nodes;	// list of known source nodes

/*
 * TR_deliver(): Deliver packets to Application Layer
 */
void TR_deliver(WINDOW *w) {
  PACKET *pkt;			// packet to deliver
  size_t len;			// length of message delivered
  
  while (window_ready(w)) {
    pkt = remove_next_packet(w);	// remove oldest packet from window
    len = pkt->len;
    CHECK(CNET_write_application(&(pkt->msg), &len));	// pass to Application Layer
    free(pkt);			// deallocate memory in window
    w->next_seq++;
  }
}

/*
 * TR_process_pkt(): Receive packet from Network Layer, deliver if ready
 * 
 * Assumptions: Header has been verified
 */
static void TR_process_packet(PACKET *pkt) {
  NODE *sn = get_node((CnetAddr)pkt->src, &src_nodes),
       *dn = get_node((CnetAddr)pkt->src, &dest_nodes);
  WINDOW *w;			// transport window for source

  if (pkt->crc_head == head_crc(pkt)) {	// if header CRC verifies OK
    if (sn == NULL) {		// if not found
      sn = new_node((CnetAddr)pkt->src, &src_nodes);	// create new
    }
    w = (sn->window);		// get window for node
    
    if (pkt->seq_ack != NOACKINFO) {	// if packet contains ACK/NAK info
      TR_confirm_packet(pkt->src, pkt->seq_ack, pkt->ack);	// confirm status
    }
    
    if (dn == NULL) {
      dn = new_node((CnetAddr)pkt->src, &dest_nodes);
    }
    
    if ((pkt->seq > 0) && (pkt->len > 0)) {
      if (pkt->crc_msg == msg_crc(pkt)) {	// if message CRC verifies OK
        add_ack(pkt, ACK, dn);	// send ACK
        
        if (in_window(pkt, w) && (get_packet(pkt->seq, (sn->window)) == NULL)) {	// if packet within window and new
          add_packet(pkt, w);	// add it
        }
        
        if (window_ready(w)) {	// if window contains packets ready for delivery
          TR_deliver(w);		// deliver packets in window
        }
      } else {			// message was corrupt
        add_ack(pkt, NAK, get_node((CnetAddr)pkt->src, &dest_nodes));
      }
    }
  }
}
